package cc.wfu.type.config;

import cc.wfu.type.exception.AccessDeniedHandlerImpl;
import cc.wfu.type.exception.AuthenticationEntryPointImpl;
import cc.wfu.type.filter.JwtAuthenticationTokenFilter;
import cc.wfu.type.service.UserDetailsServiceStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;

/**
 *  配置 Spring Security 密码加密方式
 */
@Slf4j
@Configuration("JwtAuthConfig")
@EnableWebSecurity
@Primary
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启权限控制功能
public class JwtAuthConfig extends WebSecurityConfigurerAdapter {
    private final WebAuthSecurity webAuthSecurity;
    private final HttpAuthSecurity httpAuthSecurity;

    @Resource
    private AccessDeniedHandlerImpl accessDeniedHandler;
    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;

    public JwtAuthConfig(WebAuthSecurity webAuthSecurity, HttpAuthSecurity httpAuthSecurity, AuthSecurity authSecurityService) {
        this.webAuthSecurity = webAuthSecurity;
        this.httpAuthSecurity = httpAuthSecurity;

        authSecurityService.configure(this.webAuthSecurity);
        authSecurityService.configure(this.httpAuthSecurity);
    }

    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired(required = false)
    private UserDetailsServiceStrategy detailsService;

    @Bean
    public LettuceConnectionFactory redisConnectionFactory(RedisClientConfigProperties properties) {
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(properties.getHost(), properties.getPort());
        redisConfig.setPassword(properties.getPassword());
        return new LettuceConnectionFactory(redisConfig);
    }

    /**
     * 创建并配置RedisTemplate实例
     *
     * @param connectionFactory Redis连接工厂
     * @return RedisTemplate实例
     */
    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 初始化RedisTemplate
        template.afterPropertiesSet();
        // 返回RedisTemplate实例
        return template;
    }

    static {
        log.info("【权限服务装配】");
    }

    /**
     * 路径匹配方式
     */
    @Bean
    public AntPathMatcher antPathMatcher() {
        return new AntPathMatcher();
    }

    /**
     * 配置Security加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * AuthenticationManager 用户用户验证
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 通过 userDetailsService 指定具体的 DetailsService.
     */
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        if (detailsService != null)
            auth.userDetailsService(detailsService);
    }

    public void configure(WebSecurity web) {
        web.ignoring().antMatchers(
                this.webAuthSecurity.matchers()
        );
    }


    /**
     *  放行接口
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                // 关闭csrf, 防止csrf攻击.
                .csrf().disable()
                // 不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
//                .antMatchers("/images/**").permitAll() // 放行 static 目录下的所有资源
                // 对于登录接口 允许匿名访问
                .antMatchers(httpAuthSecurity.matchers())
                .anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
        /*
         * 配置 token 拦截器
         */
        http.addFilterAfter(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        /*
         * 配置异常处理器
         */
        http
                // 认证异常
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler)
                .and()
                // 授权异常
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);

        /*
         * Spring Security 设置跨域
         */
        http.cors();
    }


}
